package com.dxysun.spring.servlet.v1;

import com.dxysun.annotation.DXYAutowired;
import com.dxysun.annotation.DXYController;
import com.dxysun.annotation.DXYRequestMapping;
import com.dxysun.annotation.DXYService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class DxyDispatchServlet extends HttpServlet
{
	private Map<String, Object> mapping = new HashMap<>();

	@Override
	public void init(ServletConfig config) throws ServletException
	{
		String contextLocation = config.getInitParameter("contextConfigLocation");
		try (InputStream in = this.getClass().getClassLoader().getResourceAsStream(contextLocation))
		{
			Properties properties = new Properties();
			properties.load(in);
			String scanPackage = properties.getProperty("scanPackage");
			doScanner(scanPackage);
			Set<String> stringSet = new HashSet<>(mapping.keySet());
			for (String className : stringSet)
			{
				if (!className.contains("."))
				{
					continue;
				}
				Class<?> clazz = Class.forName(className);
				if (clazz.isAnnotationPresent(DXYController.class))
				{
					Object object = clazz.newInstance();
					mapping.put(className, object);
					if (clazz.isAnnotationPresent(DXYRequestMapping.class))
					{
						DXYRequestMapping dxyRequestMapping = clazz.getAnnotation(DXYRequestMapping.class);
						String baseUrl = dxyRequestMapping.value();
						Method[] methods = clazz.getDeclaredMethods();
						for (Method method : methods)
						{
							if (method.isAnnotationPresent(DXYRequestMapping.class))
							{
								DXYRequestMapping dxyMethodRequestMapping = method
										.getAnnotation(DXYRequestMapping.class);
								String url = baseUrl + "/" + dxyMethodRequestMapping.value();
								mapping.put(url.replaceAll("/+", "/"), method);
								System.out.println("Mapped " + url + "," + method);
							}
						}
					}
				}
				else if (clazz.isAnnotationPresent(DXYService.class))
				{
					DXYService dxyService = clazz.getAnnotation(DXYService.class);
					String beanName = dxyService.value();
					if ("".endsWith(beanName))
					{
						beanName = clazz.getName();
					}
					Object instance = clazz.newInstance();
					mapping.put(beanName, instance);
					for (Class<?> i : clazz.getInterfaces())
					{
						mapping.put(i.getName(), instance);
					}
				}
			}
			for (Object object : mapping.values())
			{
				if (object == null)
					continue;
				if (object.getClass().isAnnotationPresent(DXYController.class))
				{
					Field[] fields = object.getClass().getDeclaredFields();
					for (Field field : fields)
					{
						if (field.isAnnotationPresent(DXYAutowired.class))
						{
							DXYAutowired dxyAutowired = field.getAnnotation(DXYAutowired.class);
							String beanName = dxyAutowired.value();
							if ("".endsWith(beanName))
							{
								beanName = field.getType().getName();
							}
							Object instance = mapping.get(beanName);
							if (instance == null)
							{
								continue;
							}
							field.setAccessible(true);
							field.set(object, instance);
						}
					}
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		System.out.println("DXY MVC Framework is init");

	}

	private void doScanner(String scanPackage)
	{
//		String path = "/" + scanPackage.replaceAll("\\.", "/");
		String path = scanPackage.replaceAll("\\.", "/");
		URL url = this.getClass().getClassLoader().getResource(path);
		if (url == null)
		{
			return;
		}
		File dirFile = new File(url.getPath());
		for (File file : dirFile.listFiles())
		{
			if (file.isDirectory())
			{
				doScanner(scanPackage + "." + file.getName());
			}
			if (file.getName().endsWith(".class"))
			{
				String clazzName = (scanPackage + "." + file.getName().replace(".class", ""));
				mapping.put(clazzName, null);
			}
		}

	}

	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
	{
		doPost(request, response);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException
	{
		try
		{
			doDispatch(request, response);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			response.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
		}
	}

	private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		if (!mapping.containsKey(url))
		{
			response.getWriter().write(url + " 404 Not Found!!");
			return;
		}
		Method method = (Method) mapping.get(url);
		Map<String, String[]> params = request.getParameterMap();
		method.invoke(mapping.get(method.getDeclaringClass().getName()), new Object[] { request, response, params.get("name")[0] });
	}

}
